package org.activequant.data.util.series;

import java.util.Iterator;

import org.activequant.core.domainmodel.marketscanner.MarketScanSeriesSpecification;
import org.activequant.core.domainmodel.marketscanner.ScanDataEntity;
import org.activequant.core.domainmodel.marketscanner.ScanDataTimeSeries;
import org.activequant.data.retrieval.IScanDataSeriesDataSource;
import org.activequant.data.retrieval.IScanSeriesDataIteratorSource;
import org.activequant.data.util.ReverseListIterator;
import org.apache.log4j.Logger;

/**
 * Converts array-values series source to iterator-values series source.
 * <br>
 * SeriesSourceToIteratorSourceAdapter&lt;T extends ScanDataEntity&lt;T&gt;&gt; implements IScanSeriesDataIteratorSource&lt;T&gt;<br>
 * Holds the following associated variables:
 * <ul>
 * <li>arrayDataSource(IScanDataSeriesDataSource&lt;? extends ScanDataTimeSeries&lt;T&gt;&gt;)</li>
 * </ul>
 *  @author Dimitar Misev
 */
public class ScanSeriesSourceToIteratorSourceAdapter<T extends ScanDataEntity<T>> implements IScanSeriesDataIteratorSource<T> {
//	public final static TimeStampFormat formatter = new TimeStampFormat("yyyy/MM/dd HH:mm:ss.SSS Z");
//	{
//		//formatter.setTimeZone(exchangeCalendar.getTimeZone());
//		formatter.setTimeZone(TimeZone.getDefault());
//	}
	private final Logger log = Logger.getLogger(getClass());
	/**
	 * private IScanDataSeriesDataSource&lt;? extends ScanDataTimeSeries&lt;T&gt;&gt; arrayDataSource;
	 */
	private IScanDataSeriesDataSource<? extends ScanDataTimeSeries<T>> arrayDataSource;
	/**
	 * returns the associated arrayDataSource(IScanDataSeriesDataSource&lt;? extends ScanDataTimeSeries&lt;T&gt;&gt;)
	 * @return
	 */
	public IScanDataSeriesDataSource<? extends ScanDataTimeSeries<T>> getArrayDataSource() {
		return arrayDataSource;
	}
	/**
	 * sets the associated arrayDataSource(IScanDataSeriesDataSource&lt;? extends TimeSeries&lt;T&gt;&gt;) with the given val(IScanDataSeriesDataSource&lt;ScanDataTimeSeries&lt;T&gt;&gt;)
	 * @param val
	 */
	public void setArrayDataSource(IScanDataSeriesDataSource<ScanDataTimeSeries<T>> val) {
		arrayDataSource = val;
	}
	/**
	 * Fetches a series(ScanDataTimeSeries&lt;T&gt;) for the given ss(MarketScanSeriesSpecification) (and its time start - end timestamp) from the associated arrayDataSource(IScanDataSeriesDataSource&lt;? extends ScanDataTimeSeries&lt;T&gt;&gt;).<br/>
	 * Creates and returns an Iterator&lt;T&gt; for the series(ScanDataTimeSeries&lt;T&gt;) (in reverse) starting from the latest ones and going towards the older data.
	 * Iterator<T>
	 * @param ss
	 * @return
	 * @throws Exception
	 */
	private Iterator<T> fetchArrayAndBuildIterator(MarketScanSeriesSpecification ss) throws Exception {
		//log.info("about to fetch interval: from " + ss.getStartTimeStamp() + " to " + ss.getEndTimeStamp());
		//log.info("about to fetch interval: from " + formatter.format(ss.getStartTimeStamp()) + " to " + formatter.format(ss.getEndTimeStamp()));
		ScanDataTimeSeries<T> series = arrayDataSource.fetch(ss);
		//log.info("fetched: " + series.size() + " entities");
		return new ReverseListIterator<T>(series);
	}
	/**
	 * Creates and returns an Iterable&lt;T&gt; to iterate (from latest to oldest) the ScanDataTimeSeries&lt;T&gt; fetched for the given ss(MarketScanSeriesSpecification) (and its time start - end timestamp)
	 * using the associated arrayDataSource(ISeriesDataSource&lt;? extends ScanDataTimeSeries&lt;T&gt;&gt;)
	 */
	public Iterable<T> fetch(final MarketScanSeriesSpecification ss) throws Exception {
		
		//log.info("fetching: " + ss);
		//log.info("fetching: " + formatter.format(ss.getStartTimeStamp()) + " to " + formatter.format(ss.getEndTimeStamp()));
		if(ss.getStartTimeStamp() == null || ss.getEndTimeStamp() == null) {
			throw new IllegalArgumentException("both 'start' date and 'end' date should be set in specification: " + ss);
		}
		/**
		 * final Iterator&lt;T&gt; iterator = new Iterator&lt;T&gt;() {...}
		 */
		final Iterator<T> iterator = new Iterator<T>() {

			private Iterator<T> arrayIterator;

			public boolean hasNext() {
				if(arrayIterator == null) {
					try {
						arrayIterator = fetchArrayAndBuildIterator(ss);
					} catch(Exception e) {
						e.printStackTrace();
						log.error(e);
						return false;
					}
				}

				return arrayIterator.hasNext();
			}

			public T next() {
				if(hasNext()) return arrayIterator.next();
				return null;
			}

			public void remove() {
				throw new UnsupportedOperationException();
			}
			
		};
		
		return new Iterable<T>() {
			public Iterator<T> iterator() {
				return iterator;
			}
		};
	}
	/**
	 * Returns "ARRAYADAPTER"
	 * @return
	 */
	public String getVendorName() {
		return "ARRAYADAPTER";
	}
}
